﻿/*************************************************
网络核心功能处理类
author：Daixiwei
**************************************************/
using System;
using System.Collections;
using System.Collections.Generic;

namespace taurus.client
{
    public enum SocketCode
    {
        /// <summary>
        /// 连接成功
        /// </summary>
        Connect,
        Disconnect,
        /// <summary>
        /// 未知异常
        /// </summary>
        Exception,
        /// <summary>
        /// 连接服务异常
        /// </summary>
        ExceptionOnConnect,
        /// <summary>
        /// 发送数据流错误
        /// </summary>
        SendError,
        /// <summary>
        /// 接收服务器数据流异常
        /// </summary>
        ExceptionOnReceive,
        /// <summary>
        /// 服务器连接超时
        /// </summary>
        TimeoutDisconnect,
        /// <summary>
        /// 服务器断开连接
        /// </summary>
        DisconnectByServer,
        /// <summary>
        /// 客户端网络异常
        /// </summary>
        NetworkException,
        /// <summary>
        /// 连接安全异常，一般为防火墙阻止
        /// </summary>
        SecurityExceptionOnConnect,
        UDPConnect,
        UDPException
    }

    public enum ConnectionProtocol
    {
        Tcp,
        Web,
    }

    public static class NetClientEvent
    {
        public const string Connect = "connect";

        public const string OnEvent = "onevent";
    }

    public class TaurusConfig
    {
        public string host;
        public string game;
        public int udpPort;
        public ConnectionProtocol protocol = ConnectionProtocol.Tcp;
    }

    public class TaurusClient
    {
        private int _id;
        private bool _connect;
        private string _gameId;
        private string _host;
        private int _udpPort;

        private ISocketLayer _scoket;
        private ThreadManager _threadManager;

        private EventDispatcher _dispatcher;
        
        private static int _tempID;
        internal bool _kill = false;
       

        private static int _getValidID()
        {
            return ++_tempID;
        }

        public TaurusClient(TaurusConfig config)
        {
            _init(config);
        }

        public TaurusClient(string host, string game, ConnectionProtocol protocol = ConnectionProtocol.Tcp)
        {
            TaurusConfig config = new TaurusConfig();
            config.host = host;
            config.game = game;
            config.protocol = protocol;
            _init(config);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="host"></param>
        /// <param name="game"></param>
        /// <param name="protocol"></param>
        void _init(TaurusConfig config)
        {
            this._udpPort = config.udpPort;
            ISocketLayer scoket = null;
            switch (config.protocol)
            {
                case ConnectionProtocol.Tcp:
                    scoket = new SocketEngine(this);
                    break;
                case ConnectionProtocol.Web:
                    scoket = new HttpClient(config.host);
                    _connect = true;
                    break;
                default:
                    scoket = new SocketEngine(this);
                    break;
            }
            __initialize(config.host, config.game, scoket);
        }

        /// <summary>
        /// 添加事件
        /// </summary>
        /// <param name="eventType"></param>
        /// <param name="listener"></param>
        public void addEventListener(string eventType, IEventListener listener)
        {
            _dispatcher.addEventListener(eventType, listener);
        }

        /// <summary>
        /// 移除所有事件
        /// </summary>
        public void removeAllEventListener()
        {
            _dispatcher.removeAllEventListener();
        }

        /// <summary>
        /// 移除指定事件
        /// </summary>
        /// <param name="eventType"></param>
        /// <param name="listener"></param>
        public void removeEventListener(string eventType, IEventListener listener)
        {
            _dispatcher.removeEventListener(eventType, listener);
        }


        private void __initialize(string host, string game, ISocketLayer socket)
        {
            _id = _getValidID();
            
            _dispatcher = new EventDispatcher();
            _host = host;
            _gameId = game;
            _threadManager = new ThreadManager();
            _threadManager.start();
            _scoket = socket;
            _scoket.setOnConnect((code) =>
            {
                if (code == SocketCode.Connect)
                    _connect = true;
                else
                {
                    _connect = false;
                    _kill = true;
                }
                    
                Event evt = new Event(NetClientEvent.Connect);
                evt.setParameter("code", code);
                _dispatcher.dispatchEvent(evt);
            });
            _scoket.setOnEvent((msg) =>
            {
                if (_connect)
                {
                    Event evt = new Event(NetClientEvent.OnEvent);
                    evt.setParameter("msg", msg);
                    _dispatcher.dispatchEvent(evt);
                    if (NetManager.DebugPrint)
                    {
                        var str = _scoket.getProtocol() == ConnectionProtocol.Web ? "WEB" : "TCP";
                        var data_str = msg.textParam!=null? msg.textParam : (msg.param != null ? msg.param.toJson() : "null");
                        Logger.debug("Event [" + str + "->" + msg.command + "] data:" + data_str);
                    }
                }
            });
            NetManager.addNetClient(this);
        }

        /// <summary>
        /// 连接服务器
        /// </summary>
        public void connect()
        {
            _scoket.connect();
        }

        public void connect(ITObject validate)
        {
            _scoket.connect(validate);
        }

        /// <summary>
        /// Kill net connection.
        /// </summary>
        public void killConnection()
        {
            removeAllEventListener();
            //_responseMap.Clear();
            _threadManager.stop();
            _scoket.disconnect();
            _connect = false;
            _kill = true;
        }

        /// <summary>
        /// 发送网络消息包
        /// </summary>
        /// <param name="msg">消息数据对象</param>
        protected void send(Message msg)
        {
            if (msg == null)
                return;
            if (NetManager.DebugPrint)
            {
                var str = getProtocol() == ConnectionProtocol.Web ? "WEB" : "TCP";
                var data_str = msg.textParam != null ? msg.textParam : (msg.param != null ? msg.param.toJson() : "null");
                str = "Requst [" + str + "->" + msg.command + "] ->param:" + data_str;
                Logger.debug(str);
            }
            _threadManager.enqueueSend(sendData, msg);
        }

        public Message sendToUdp(string cmd, ITObject param)
        {
            Message msg = new Message();
            msg.command = cmd;
            msg.param = param;
            msg.useUdp = true;
            msg.useText = false;
            this.send(msg);
            return msg;
        }

        /// <summary>
        /// 发送网络消息包
        /// </summary>
        /// <param name="cmd">网络协议指令Key</param>
        /// <param name="param">参数</param>
        /// <param name="callback">网络消息回调</param>
        /// <returns></returns>
        public Message send(string cmd, ITObject param, Action<MessageResponse> callback)
        {
            Message msg = new Message();
            msg.command = cmd;
            msg.param = param;
            msg.callback = callback;
            msg.useText = false;
            this.send(msg);
            return msg;
        }

        /// <summary>
        /// 发送网络消息包
        /// </summary>
        /// <param name="cmd">网络协议指令Key</param>
        /// <param name="text">JSON 文本</param>
        /// <param name="callback">网络消息回调</param>
        public Message sendText(string cmd, string text, Action<MessageResponse> callback)
        {
            var msg = new Message();
            msg.useText = true;
            msg.command = cmd;
            msg.textParam = text;
            msg.callback = callback;
            this.send(msg);
            return msg;
        }

        /// <summary>
        /// 发送数据线程方法
        /// </summary>
        /// <param name="msg"></param>
        private void sendData(Message msg)
        {
            _scoket.write(msg);
        }

        /// <summary>
        /// 处理事件
        /// </summary>
        internal void processEvent()
        {
            _scoket.processEvent();
        }

        /// <summary>
        /// 清理响应回调
        /// </summary>
        public void clearResponse()
        {
            _scoket.clearResponse();
        }

        /// <summary>
        /// The net client id
        /// </summary>
        /// <returns></returns>
        public int getId()
        {

            return _id;

        }

        /// <summary>
        /// 获取服务地址
        /// </summary>
        /// <returns></returns>
        public string getHost()
        {
            return _host;
        }

        /// <summary>
        /// 获取udp端口
        /// </summary>
        /// <returns></returns>
        public int getUdpPort()
        {
            return _udpPort;
        }

        /// <summary>
        /// The curren Game ID
        /// </summary>
        /// <returns></returns>
        public string getGameID()
        {
            return _gameId;
        }

        /// <summary>
        /// 网络是否已连接
        /// </summary>
        /// <returns></returns>
        public bool isConnected()
        {
            return _connect;
        }

        /// <summary>
        /// 当前网络类型
        /// </summary>
        /// <returns></returns>
        public ConnectionProtocol getProtocol()
        {
            return _scoket.getProtocol();
        }

        /// <summary>
        /// 网络延时
        /// </summary>
        /// <returns></returns>
        public int getAveragePingTime()
        {
            return _scoket.getAveragePingTime();
        }

        /// <summary>
        /// 获取服务器Session字符
        /// </summary>
        /// <returns></returns>
        public string getSession()
        {
            return _scoket.getSession();
        }

        /// <summary>
        /// 设置服务器Session字符
        /// </summary>
        /// <param name="session"></param>
        public void setSession(string session)
        {
            _scoket.setSession(session);
        }

    }
}
